package com.dsbj.apolo.facecollector.service;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dsbj.apolo.facecollector.entity.CollectInfo;
import com.dsbj.apolo.facecollector.mapper.CollectInfoMapper;
import com.dsbj.apolo.facecollector.utils.ExcelUtils;
import com.dsbj.common.msg.ResponseMessage;
import com.uniubi.uface.JudgeResult;
import com.uniubi.uface.QualityJudge;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.core.util.UuidUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Service
public class FaceCollectService {

    @Value(value = "${faceImgPath}")
    private String faceImgPath;

    @Value(value = "${zipTempPath}")
    private String zipTempPath;

    @Autowired
    private CollectInfoMapper collectInfoMapper;

    @Transactional
    public ResponseMessage processCollectInfo(CollectInfo collectInfo) {
        if(StringUtils.isBlank(collectInfo.getPhotoBase64())){
            return ResponseMessage.error("人脸数据为空");
        }

        ///先检查身份证号和工号是否匹配
        QueryWrapper<CollectInfo> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("ID_NO",collectInfo.getIdNo()).eq("EMPLOYEE_NO",collectInfo.getEmployeeNo());
        CollectInfo oldInfo=collectInfoMapper.selectOne(queryWrapper);
        if(oldInfo==null){
            return ResponseMessage.error("身份证号和工号不匹配");
        }

        byte [] byteArray = base64ToBytes(collectInfo.getPhotoBase64());

        QualityJudge judge = new QualityJudge(48);
        JudgeResult judgeResult = judge.judgeImage(byteArray);
        printJudgeResult(judgeResult);
        judge.release();

        if(judgeResult.code==0){
            String imagePath = faceImgPath+collectInfo.getIdNo()+".jpg";
            if(saveBase64ToImageFile(byteArray,imagePath)){
                ///
                oldInfo.setCheckResult(1);
                oldInfo.setCollectDate(new Date());
                collectInfoMapper.updateById(oldInfo);
            }
        }else {
            String reason;
            switch (judgeResult.code){
                case 2:reason="输入为空";break;
                case -1:reason="检测不到人脸";break;
                case -2:reason="检测到多张人脸";break;
                case -3:reason="人脸太小";break;
                case -4:reason="人脸越界";break;
                case -5:reason="人脸角度不符合要求";break;
                case -6:reason="人脸光照不符合要求";break;
                default:reason="未知原因";break;
            }
            return ResponseMessage.error("人脸质量不合格,请重新上传.详细原因: "+reason);
        }



        /*QualityJudge judge = new QualityJudge(48);

        JudgeResult judgeResult = judge.judgeImage(imagePath);
        printJudgeResult(judgeResult);

        judge.release();*/
        return ResponseMessage.ok();
    }

    public byte[] base64ToBytes(String imgStr){
        BASE64Decoder decoder = new BASE64Decoder();
        try
        {
            //Base64解码
            byte[] byteArray = decoder.decodeBuffer(imgStr);
            for(int i=0;i<byteArray.length;++i)
            {
                if(byteArray[i]<0)
                {//调整异常数据
                    byteArray[i]+=256;
                }
            }
            return byteArray;
        }
        catch (Exception e)
        {
            return null;
        }
    }
    private void printJudgeResult(JudgeResult judgeResult) {
        System.out.println("code : " + judgeResult.code);

        System.out.println("xmin : " + judgeResult.xmin);
        System.out.println("xmax : " + judgeResult.xmax);
        System.out.println("ymin : " + judgeResult.ymin);
        System.out.println("ymax : " + judgeResult.ymax);

        System.out.println("pitch : " + judgeResult.pitch);
        System.out.println("yaw : " + judgeResult.yaw);
        System.out.println("roll : " + judgeResult.roll);

        System.out.println("light : " + judgeResult.light);
        System.out.println("blur : " + judgeResult.blur);
        System.out.println("yinyang : " + judgeResult.yinyang);
    }

    private boolean saveBase64ToImageFile(String imgStr,String imagePath)
    {  //对字节数组字符串进行Base64解码并生成图片
        if (imgStr == null) //图像数据为空
            return false;
        BASE64Decoder decoder = new BASE64Decoder();
        try
        {
            //Base64解码
            byte[] b = decoder.decodeBuffer(imgStr);
            for(int i=0;i<b.length;++i)
            {
                if(b[i]<0)
                {//调整异常数据
                    b[i]+=256;
                }
            }
            //生成jpeg图片
            OutputStream out = new FileOutputStream(imagePath);
            out.write(b);
            out.flush();
            out.close();
            return true;
        }
        catch (Exception e)
        {
            return false;
        }
    }

    private boolean saveBase64ToImageFile(byte[] byteArray,String imagePath)
    {
        try
        {
            //生成jpeg图片
            OutputStream out = new FileOutputStream(imagePath);
            out.write(byteArray);
            out.flush();
            out.close();
            return true;
        }
        catch (Exception e)
        {
            return false;
        }
    }

    public ResponseMessage getCollectInfoByIdNo(String idNo) {

        QueryWrapper<CollectInfo> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("ID_NO",idNo);
        CollectInfo collectInfo=collectInfoMapper.selectOne(queryWrapper);
        return ResponseMessage.ok(collectInfo);
    }

    @Transactional
    public void processExcel(MultipartFile file) throws IOException {
        List<List<Object>> list = ExcelUtils.getExcelData(file.getInputStream(),0,0);

        for(List<Object> subList:list){
            String idNo = (String) subList.get(0);
            String employeeNo = (String) subList.get(1);

            ///检查数据库有无此员工
            ResponseMessage<CollectInfo> rsp = getCollectInfoByIdNo(idNo);
            if(rsp.getData()!=null){
                CollectInfo collectInfo = rsp.getData();
                collectInfo.setCheckResult(0);
                collectInfo.setUpdateTime(new Date());
                collectInfo.setCollectDate(null);
                collectInfoMapper.updateById(collectInfo);
                deletePhoto(idNo);
            }else{
                CollectInfo collectInfo = new CollectInfo();
                collectInfo.setIdNo(idNo);
                collectInfo.setEmployeeNo(employeeNo);
                collectInfo.setUpdateTime(new Date());
                collectInfo.setCheckResult(0);
                collectInfoMapper.insert(collectInfo);
            }
        }
    }

    private void deletePhoto(String idNo) {
        String imagePath = faceImgPath+idNo+".jpg";
        File file = new File(imagePath);
        if(file.exists()){
            file.delete();
        }
    }

    public void downloadFaces(HttpServletResponse response) {

        File qualifiedFile = new File(faceImgPath);
        List<File> fileList = new ArrayList<>();
        File[] files = qualifiedFile.listFiles();
        for(File f:files){
            if(f.isFile()){
                if (f.getName().toLowerCase().endsWith(".jpg")){
                    fileList.add(f);
                }
            }
        }
        downLoadFiles(fileList, response);
    }

    private void downLoadFiles(List<File> fileList, HttpServletResponse response) {
        try {
            // List<File> 作为参数传进来，就是把多个文件的路径放到一个list里面
            // 创建一个临时压缩文件

            // 临时文件可以放在CDEF盘中，但不建议这么做，因为需要先设置磁盘的访问权限，最好是放在服务器上，方法最后有删除临时文件的步骤

            String zipFilename = zipTempPath+ UUID.randomUUID()+".zip";
            File file = new File(zipFilename);
            file.createNewFile();
            if (!file.exists()) {
                file.createNewFile();
            }
            response.reset();
            // response.getWriter()
            // 创建文件输出流
            FileOutputStream fous = new FileOutputStream(file);
            ZipOutputStream zipOut = new ZipOutputStream(fous);
            zipFile(fileList, zipOut);
            zipOut.close();
            fous.close();
            downloadZip(file, response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void downloadZip(File file, HttpServletResponse response) {
        try {
            // 以流的形式下载文件。
            InputStream fis = new BufferedInputStream(new FileInputStream(file.getPath()));
            byte[] buffer = new byte[fis.available()];
            fis.read(buffer);
            fis.close();
            // 清空response
            response.reset();

            OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
            response.setContentType("application/octet-stream");

            // 如果输出的是中文名的文件，在此处就要用URLEncoder.encode方法进行处理
            response.setHeader("Content-Disposition",
                    "attachment;filename=" + new String(file.getName().getBytes("GB2312"), "ISO8859-1"));
            toClient.write(buffer);
            toClient.flush();
            toClient.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            try {
                File f = new File(file.getPath());
                f.delete();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private void zipFile(List<File> fileList, ZipOutputStream zipOut) {
        int size = fileList.size();
        for (int i = 0; i < size; i++) {
            File file = (File) fileList.get(i);
            zipFile(file, zipOut);
        }
    }

    private void zipFile(File inputFile, ZipOutputStream zipOut) {
        try {
            if (inputFile.exists()) {
                if (inputFile.isFile()) {
                    FileInputStream IN = new FileInputStream(inputFile);
                    BufferedInputStream bins = new BufferedInputStream(IN, 512);
                    ZipEntry entry = new ZipEntry(inputFile.getName());
                    zipOut.putNextEntry(entry);
                    // 向压缩文件中输出数据
                    int nNumber;
                    byte[] buffer = new byte[512];
                    while ((nNumber = bins.read(buffer)) != -1) {
                        zipOut.write(buffer, 0, nNumber);
                    }
                    // 关闭创建的流对象
                    bins.close();
                    IN.close();
                } else {
                    try {
                        File[] files = inputFile.listFiles();
                        for (int i = 0; i < files.length; i++) {
                            zipFile(files[i], zipOut);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
